﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Threading;
using System.ComponentModel;
using System.Windows.Shapes;

namespace FlowControl
{
    class Procs
    {
        private object[] Out = new object[3];                           // References to next objects
        private int[] OutRelIn = new int[3];                            // Relations of Outi to the next Ini
        public int Ident;                                               // Actuall object ident
        private int connected = 0;                                      // Number of connected OUTs
        private object RouterCommonPoint = null;                        // It store common point as object
        private object[] PreCommonPoint = new object[3];                // Reference of pre object (using in common point section)
        private bool Stopped = false;                                   // If true, do not enter to the next object (using with CheckRoute())
        public Label status = null;                                     // In graph-obj Label reference
        public TextBox statusBox = null;                                  // In graph-obj TextBox reference
        public Rectangle progress = null;                               // In graph-obj Progress-rect reference
        private bool[] OutLink = new bool[3];                           // If OutLinN is true, OutN is linked
        private List<List<object>> COut = new List<List<object>>();     // Traceroute for each OUT
        public double[,] Result = null;                                 // Result table as end of calculating in module
        public double[][,] In = new double[3][,];                       // InI table (I must remember to changer it to private!)




        /*public Procs(int Ident, Canvas canv)
        {
            this.Ident = Ident;
            //this.status = (Label)canv.FindName("labelStatus_" + this.Ident);
            //this.progress = (Rectangle)canv.FindName("rectP_" + this.Ident);
        }*/
        public Procs()
        {

        }

        // Add next object reference to Outi
        public void AddOut(int OutI, int InI, object Out)
        {
            connected++;
            this.Out[OutI] = Out;
            this.OutRelIn[OutI] = InI;
        }

        // Delete reference from Outi
        public void DelOut(int OutI)
        {
            connected--;
            this.Out[OutI] = null;
            this.OutRelIn[OutI] = -1;
        }

        // View reference in Outi
        public object ViewOut(int i)
        {
            return Out[i];
        }

        // Start method used by calc-modules
        virtual public void Start()
        {
            
        }

        // Run next object
        public void Next()
        {
            // Run module's method
            Start();

            // Stop running object if common point has found
            if (Stopped)
                return;

            // Check route for find to diffrent ways
            CheckRoute();

            // Router
            if (this.connected == 1)            // one way
            {
                for (int i = 0; i < 3; i++)
                {
                    if (Out[i] != null)
                    {
                        ((Procs)Out[i]).In[this.OutRelIn[i]] = (double[,])(this.Result.Clone());     // send result to the next object
                        ((Procs)Out[i]).Next();                                                      // run next object!

                        return;
                    }
                }
            }
            else if (this.connected > 1)        // multi-way (and multi-proc)
            {
                if (this.RouterCommonPoint != null)           // support for common point -< >-
                {
                    Thread[] T = new Thread[3];
                    int[] OutRelInTemp;


                    for (int i = 0; i < 3; i++)
                    {
                        if (!OutLink[i] && Out[i] != null)  // normal multi-mode, if one of OUTi haven't got common point
                        {
                            ((Procs)Out[i]).In[this.OutRelIn[i]] = (double[,])(this.Result.Clone());     // send result to the next object

                            Thread Tn = new Thread(((Procs)Out[i]).Next);
                            Tn.IsBackground = true;
                            Tn.Start();
                        }
                        else if (OutLink[i])                // common point
                        {
                            ((Procs)Out[i]).In[this.OutRelIn[i]] = (double[,])(this.Result.Clone());     // send result to the next object

                            T[i] = new Thread(((Procs)Out[i]).Next);
                            T[i].IsBackground = true;
                            T[i].Start();
                        }
                    }

                    // ---wait for threads
                    for (int i = 0; i < 3; i++)
                    {
                        if (OutLink[i])
                            T[i].Join();
                    }

                    // send result to the common point
                    for (int i = 0; i < 3; i++)
                    {
                        if (OutLink[i])                // common point
                        {
                            OutRelInTemp = ((Procs)PreCommonPoint[i]).OutRelIn;

                            for (int j = 0; j < 3; j++)
                            {
                                if (((Procs)PreCommonPoint[i]).Out[j] == RouterCommonPoint)
                                    ((Procs)RouterCommonPoint).In[OutRelInTemp[j]] = (double[,])(((Procs)PreCommonPoint[i]).Result.Clone());
                            }
                        }
                    }

                    // run common point!
                    ((Procs)RouterCommonPoint).Next();

                    return;
                }
                else  // normal multi-mode
                {
                    for (int i = 0; i < 3; i++)
                    {
                        if (Out[i] != null)
                        {
                            ((Procs)Out[i]).In[this.OutRelIn[i]] = (double[,])(this.Result.Clone());     // send result to the next object

                            Thread T = new Thread(((Procs)Out[i]).Next);
                            T.IsBackground = true;
                            T.Start();
                        }
                    }
                }
            }
            else
                return;
        }

        // Check route
        private void CheckRoute()
        {
            if (this.connected > 1)
            {
                object temp;
                
                COut.Add(new List<object>());
                COut.Add(new List<object>());
                COut.Add(new List<object>());
                this.OutLink[0] = false;
                this.OutLink[1] = false;
                this.OutLink[2] = false;


                // Store route for OUTi
                for (int i = 0; i < 3; i++)
                {
                    if (this.Out[i] != null)
                    {
                        temp = this.Out[i];

                        while (temp != null)
                        {
                            COut[i].Add(temp);

                            if (((Procs)temp).Out[0] != null)
                                temp = ((Procs)temp).Out[0];
                            else if (((Procs)temp).Out[1] != null)
                                temp = ((Procs)temp).Out[1];
                            else if (((Procs)temp).Out[2] != null)
                                temp = ((Procs)temp).Out[2];
                            else
                                temp = null;
                        }
                    }
                }

                // Check nodes in OUTs
                CheckNodes(0, 1);
                CheckNodes(1, 2);
                CheckNodes(0, 2);
            }
        }

        private void CheckNodes(int a, int b)
        {
            // This procedure checks nodes in routes of OUTa and OUTb
            // Result is stored in: <bool this.OutLink[n]> and <bool this.CommonPoint>
            for (int i = 0; i < COut[a].Count; i++)
                for (int j = 0; j < COut[b].Count; j++)
                {
                    if (COut[a][i] == COut[b][j])
                    {
                        this.OutLink[a] = true;
                        this.OutLink[b] = true;
                        this.RouterCommonPoint = COut[a][i];

                        // Store pre objects
                        PreCommonPoint[a] = COut[a][i - 1];
                        PreCommonPoint[b] = COut[b][j - 1];

                        // Stop running objects
                        ((Procs)PreCommonPoint[a]).Stopped = true;
                        ((Procs)PreCommonPoint[b]).Stopped = true;

                        return;
                    }
                }
        }

        public void ChangeProgress(int progress)
        {
            if (progress > 100)
                progress = 100;
            else if (progress < 0)
                progress = 0;

            int width = (progress * 148) / 100;

            this.ChangeProgressDis(width);
        }

        private delegate void DChangeStatus(string txt);
        public void ChangeStatus(string txt)
        {
            if (this.status.Dispatcher.CheckAccess())
                this.status.Content = txt;
            else
                this.status.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new DChangeStatus(this.ChangeStatus), txt);
        }

        private delegate void DChangeProgressDis(int width);
        private void ChangeProgressDis(int width)
        {
            if (this.status.Dispatcher.CheckAccess())
                this.progress.Width = width;
            else
                this.status.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new DChangeProgressDis(ChangeProgressDis), width);
        }

        public void ChangeStatusBox(string txt)
        {
            if (this.statusBox.Dispatcher.CheckAccess())
                this.statusBox.Text += txt;
            else
                this.statusBox.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new DChangeStatus(this.ChangeStatusBox), txt);
        }
    }
}
